package tech

import (
	"fmt"
	"slices"
	"ssp/set"
)

func (s *Solve) JuniorExocet(g *Grid) (used bool, results []*Result) {
	// Helper function to process houses (rows/columns) and accumulate results
	processHouses := func(houses []House) {
		for idx := range houses { // idx is rowNum or colNum depending on houses type
			if ok, rs := g.juniorExocetBase(houses, idx); ok {
				used = true
				results = append(results, rs...)
			}
		}
	}

	// Process rows and columns using the shared helper
	processHouses(g.rows[:])
	processHouses(g.cols[:])

	return
}

func (g *Grid) juniorExocetBase(rows []House, baseRowNum int) (used bool, results []*Result) {
	baseColNumss := findBaseCells(rows[baseRowNum])
	for _, baseColNums := range baseColNumss {
		baseRow := rows[baseRowNum]
		baseCells := []*Cell{baseRow.cells[baseColNums[0]], baseRow.cells[baseColNums[1]]}
		baseCands := unionCellCands(baseCells)

		baseStackNum := baseColNums[0] / 3
		targetStackNums := []int{(baseStackNum + 1) % 3, (baseStackNum + 2) % 3}
		startRowNum := baseRowNum / 3 * 3
		targetRowNums := []int{startRowNum + (baseRowNum+1)%3, startRowNum + (baseRowNum+2)%3}
		tierNum := baseRowNum / 3
		crossTierNums := []int{(tierNum + 1) % 3, (tierNum + 2) % 3}
		crossRowNums := []int{}
		for _, tierNum := range crossTierNums {
			for i := range 3 {
				crossRowNums = append(crossRowNums, tierNum*3+i)
			}
		}

		targetColNums1 := []int{targetStackNums[0] * 3, targetStackNums[0]*3 + 1, targetStackNums[0]*3 + 2}
		targetCells1, companionCells1, targetColNums1 := getTargetCells(baseCands, rows, targetRowNums, targetColNums1, crossRowNums)

		targetColNums2 := []int{targetStackNums[1] * 3, targetStackNums[1]*3 + 1, targetStackNums[1]*3 + 2}
		targetCells2, companionCells2, targetColNums2 := getTargetCells(baseCands, rows, targetRowNums, targetColNums2, crossRowNums)

		if len(targetCells1) == 0 || len(targetCells2) == 0 {
			continue // 无有效目标单元格，跳过当前循环
		}
		if len(targetCells1) != len(targetColNums1) || len(targetCells2) != len(targetColNums2) {
			continue // 数据不一致，跳过当前循环
		}

		crossColNum3 := baseColNums[0]/3*3 + 3 - baseColNums[0]%3 - baseColNums[1]%3

		for i, crossColNum1 := range targetColNums1 {
			for j, crossColNum2 := range targetColNums2 {
				crossColNums := []int{crossColNum1, crossColNum2, crossColNum3}
				ok, crossLines := verifyCrossLines(baseCands, rows, crossRowNums, crossColNums)
				if !ok {
					continue
				}
				targetCells := []*Cell{targetCells1[i], targetCells2[j]}
				companionCells := []*Cell{companionCells1[i], companionCells2[j]}
				//fmt.Println("crossColNums", crossColNums, "crossRowNums", crossRowNums)
				//fmt.Println(baseCells, targetCells, companionCells)
				result := new(Result)
				result.addHelpCells(baseCells)
				result.addHelpCells(append(targetCells, companionCells...))
				result.addHelpCells(crossLines)
				g1 := result.getHelpCandsGroup()
				baseCands := unionCellCands(baseCells)
				g1.add(baseCands, baseCells)
				g1.add(baseCands, targetCells)
				g1.add(baseCands, crossLines)
				if companionCells[0].solved() {
					if targetCells[0].canRemoveCands(baseCands.Complement()) {
						result.addRemoved(baseCands.Complement(), targetCells[0:1])
						used = true
						//fmt.Println("used:", used, "line:", 77)
					}
				} else {
					if ok, cands := g.findOnlyCandsInTargets(baseCands, targetCells[0], companionCells[0]); ok {
						rmCands := baseCands.Complement().Diff(cands)
						if targetCells[0].canRemoveCands(rmCands) || companionCells[0].canRemoveCands(rmCands) {
							result.addRemoved(rmCands, []*Cell{targetCells[0], companionCells[0]})
							g2 := result.getHelpCandsGroup()
							g2.add(cands, []*Cell{targetCells[0], companionCells[0]})
							g1.add(baseCands, []*Cell{companionCells[0]})
							used = true
							//fmt.Println("used:", used, "line:", 88)
						}
					} else {
						cells := []*Cell{}
						zCells := []*Cell{targetCells[0], companionCells[0]}
						for _, cell := range g.boxes[targetCells[0].boxNum].cells {
							if !cell.solved() && !cell.isMemberOf(zCells) {
								cells = append(cells, cell)
							}
						}
						//fmt.Println("else:", zCells)
						if ok, subset, subsetCands, rmCells := g.verifyJENaked(baseCands, zCells, cells); ok {
							//fmt.Println("else:", subset, subsetCands, rmCells)
							if subsetCands.IsNull() {
								fmt.Println("subsetCands is null")
								continue
							}
							if len(rmCells) == 0 {
								fmt.Println("rmCells is null")
								continue
							}
							result.addRemoved(subsetCands, rmCells)
							result.addRemoved(subsetCands.Diff(baseCands), zCells)
							g3 := result.getHelpCandsGroup()
							g3.add(subsetCands, subset)
							result.addHelpCells(subset)
							used = true
							//fmt.Println("used:", used, "line:", 107)
						}
					}
				}
				if companionCells[1].solved() {
					if targetCells[1].canRemoveCands(baseCands.Complement()) {
						result.addRemoved(baseCands.Complement(), targetCells[1:])
						used = true
						//fmt.Println("used:", used, "line:", 115)
					}
				} else {
					if ok, cands := g.findOnlyCandsInTargets(baseCands, targetCells[1], companionCells[1]); ok {
						rmCands := baseCands.Complement().Diff(cands)
						if targetCells[1].canRemoveCands(rmCands) || companionCells[1].canRemoveCands(rmCands) {
							result.addRemoved(baseCands.Complement().Diff(cands), []*Cell{targetCells[1], companionCells[1]})
							g2 := result.getHelpCandsGroup()
							g2.add(cands, []*Cell{targetCells[1], companionCells[1]})
							g1.add(baseCands, []*Cell{companionCells[1]})
							used = true
							//fmt.Println("used:", used, "line:", 126)
						}
					} else {
						cells := []*Cell{}
						zCells := []*Cell{targetCells[1], companionCells[1]}
						for _, cell := range g.boxes[targetCells[1].boxNum].cells {
							if !cell.solved() && !cell.isMemberOf(zCells) {
								cells = append(cells, cell)
							}
						}
						if ok, subset, subsetCands, rmCells := g.verifyJENaked(baseCands, zCells, cells); ok {
							result.addRemoved(subsetCands, rmCells)
							result.addRemoved(subsetCands.Diff(baseCands), zCells)
							g3 := result.getHelpCandsGroup()
							g3.add(subsetCands, subset)
							result.addHelpCells(subset)
							used = true
							//fmt.Println("used:", used, "line:", 143)
						}
					}
				}
				//fmt.Println("used:", used)
				if !used {
					continue
				}
				result.setStrategyName("Junior Exocet")
				results = append(results, result)

			}
		}

	}
	return
}

func getTargetCells(baseCands set.Set, rows []House, rowNums, colNums, crossRowNums []int) (
	targetCells, companionCells []*Cell, targetCols []int) {
	for _, colNum := range colNums {
		for i, rowNum := range rowNums {
			targetCell := rows[rowNum].cells[colNum]
			if targetCell.solved() {
				continue
			}
			count := baseCands.Diff(targetCell.cands).Count()
			if count > 1 {
				continue
			}
			if count == 1 {
				missCand := baseCands.Diff(targetCell.cands).GetList()[0]
				if !existMissingCandSolved(rows, crossRowNums, colNum, missCand) {
					continue
				}
			}
			companionCell := rows[rowNums[1-i]].cells[colNum]
			targetCells = append(targetCells, targetCell)
			companionCells = append(companionCells, companionCell)
			targetCols = append(targetCols, colNum)
		}
	}
	return
}

/*
	func (g *Grid) je(rows []House, baseRowNum int) (used bool, results []*Result) {
		baseColNumss := findBaseCells(rows[baseRowNum])
		//fmt.Println(baseRowNum, "baseColNumss", baseColNumss)
		for _, baseColNums := range baseColNumss {
			//fmt.Println(baseRowNum, baseColNums)
			ok, baseCells, targetCells, companionCells, crossLines := findTargetCells(rows, baseRowNum, baseColNums)
			if !ok {
				continue
			}

			result := new(Result)
			g1 := result.getHelpCandsGroup()
			baseCands := unionCellCands(baseCells)
			g1.add(baseCands, baseCells)
			g1.add(baseCands, targetCells)
			g1.add(baseCands, crossLines)
			if companionCells[0].solved() {
				if targetCells[0].canRemoveCands(baseCands) {
					result.addRemoved(baseCands.Complement(), targetCells[0:1])
					used = true
				}
			} else {
				if ok, cands := g.findOnlyCandsInTargets(baseCands, targetCells[0], companionCells[0]); ok {
					rmCands := baseCands.Complement().Diff(cands)
					if targetCells[0].canRemoveCands(rmCands) || companionCells[0].canRemoveCands(rmCands) {
						result.addRemoved(rmCands, []*Cell{targetCells[0], companionCells[0]})
						g2 := result.getHelpCandsGroup()
						g2.add(cands, []*Cell{targetCells[0], companionCells[0]})
						g1.add(baseCands, []*Cell{companionCells[0]})
						used = true
					}
				}
			}
			if companionCells[1].solved() {
				if targetCells[1].canRemoveCands(baseCands) {
					result.addRemoved(baseCands.Complement(), targetCells[1:])
					used = true
				}
			} else {
				if ok, cands := g.findOnlyCandsInTargets(baseCands, targetCells[1], companionCells[1]); ok {
					rmCands := baseCands.Complement().Diff(cands)
					if targetCells[1].canRemoveCands(rmCands) || companionCells[1].canRemoveCands(rmCands) {
						result.addRemoved(baseCands.Complement().Diff(cands), []*Cell{targetCells[1], companionCells[1]})
						g2 := result.getHelpCandsGroup()
						g2.add(cands, []*Cell{targetCells[1], companionCells[1]})
						g1.add(baseCands, []*Cell{companionCells[1]})
						used = true
					}
				}
			}
			if !used {
				continue
			}
			result.addHelpCells(baseCells)
			result.addHelpCells(append(targetCells, companionCells...))
			result.addHelpCells(crossLines)
			result.setStrategyName("Junior Exocet")
			results = append(results, result)

		}
		return
	}
*/
func (g *Grid) findOnlyCandsInTargets(baseCands set.Set, a, b *Cell) (ok bool, onlyCands set.Set) {
	var line House
	if a.rowNum == b.rowNum {
		line = g.rows[a.rowNum]
	} else if a.colNum == b.colNum {
		line = g.cols[a.colNum]
	} else {
		return
	}
	cands := a.cands.Union(b.cands).Diff(baseCands)
	for _, cand := range cands.GetList() {
		if onlyCellsHasCand(cand, line, []*Cell{a, b}) {
			onlyCands.Add(cand)
			ok = true
		}
	}
	return
}

func findBaseCells(row House) (baseColNumss [][2]int) {
	for start := 0; start < 9; start += 3 {
		count := 0
		var solvedColNum int
		for i := start; i < start+3; i++ {
			if row.cells[i].solved() {
				count++
				solvedColNum = i
			}
		}
		if count == 1 {
			colNums := [2]int{start + (solvedColNum+1)%3, start + (solvedColNum+2)%3}
			cands := row.cells[colNums[0]].cands.Union(row.cells[colNums[1]].cands)
			if cands.Count() < 3 || cands.Count() > 5 {
				continue
			}
			baseColNumss = append(baseColNumss, colNums)
		}
	}
	return
}

/*
func findTargetCells(rows []House, baseRowNum int, baseColNums [2]int) (

		ok bool, baseCells, targetCells, companionCells, crossLines []*Cell) {
		fmt.Println(baseRowNum, baseColNums)
		row := rows[baseRowNum]
		baseCells = []*Cell{row.cells[baseColNums[0]], row.cells[baseColNums[1]]}
		baseCands := unionCellCands(baseCells)
		baseStackNum := baseColNums[0] / 3
		targetStackNums := [2]int{(baseStackNum + 1) % 3, (baseStackNum + 2) % 3}
		startRowNum := baseRowNum / 3 * 3
		targetRowNums := [2]int{startRowNum + (baseRowNum+1)%3, startRowNum + (baseRowNum+2)%3}
		tierNum := baseRowNum / 3
		crossTierNums := [2]int{(tierNum + 1) % 3, (tierNum + 2) % 3}
		crossRowNums := []int{}
		for _, tierNum := range crossTierNums {
			for i := range 3 {
				crossRowNums = append(crossRowNums, tierNum*3+i)
			}
		}
		for i, rowNum1 := range targetRowNums {
			rowNum2 := targetRowNums[1-i]
			for colNum1 := targetStackNums[0] * 3; colNum1 < targetStackNums[0]*3+3; colNum1++ {
				targetCell1 := rows[rowNum1].cells[colNum1]
				if targetCell1.solved() {
					continue
				}
				count := baseCands.Diff(targetCell1.cands).Count()
				if count > 1 {
					continue
				}
				if count == 1 {
					missCand := baseCands.Diff(targetCell1.cands).GetList()[0]
					if !existMissingCandSolved(rows, crossRowNums, colNum1, missCand) {
						continue
					}
				}

				fmt.Println("targetCell1", targetCell1)
				for colNum2 := targetStackNums[1] * 3; colNum2 < targetStackNums[1]*3+3; colNum2++ {
					/*if !rows[rowNum1].cells[colNum2].solved() {
						continue
					}
					targetCell2 := rows[rowNum2].cells[colNum2]
					if targetCell2.solved() {
						continue
					}
					count := baseCands.Diff(targetCell2.cands).Count()
					if count > 1 {
						continue
					}
					if count == 1 {
						missCand := baseCands.Diff(targetCell2.cands).GetList()[0]
						if !existMissingCandSolved(rows, crossRowNums, colNum2, missCand) {
							continue
						}
					}
					if targetCell1.cands == baseCands && targetCell2.cands == baseCands {
						continue
					}
					targetCells = []*Cell{targetCell1, targetCell2}
					companionCells = []*Cell{rows[rowNum2].cells[colNum1], rows[rowNum1].cells[colNum2]}
					fmt.Println("targetCell2", targetCell2)
					crossColNums := []int{colNum1, colNum2, baseColNums[0]/3*3 + 3 - baseColNums[0]%3 - baseColNums[1]%3}
					ok, crossLines = verifyCrossLines(baseCands, rows, crossRowNums, crossColNums)
					if ok {
						return
					}
				}
			}
		}

		return
	}
*/
func verifyCrossLines(baseCands set.Set, rows []House, rowNums []int, colNums []int) (ok bool, crosslines []*Cell) {
	candCounts := map[int]int{}
	for _, rowNum := range rowNums {
		for _, cand := range baseCands.GetList() {
			count := 0
			for _, colNum := range colNums {
				cell := rows[rowNum].cells[colNum]
				if cell.solved() {
					if cell.value == toValue(cand) {
						count++
					}
				} else {
					if cell.cands.Has(cand) {
						count++
					}
				}
			}
			if count > 0 {
				candCounts[cand]++
			}
		}
	}
	for cand, count := range candCounts {
		if count < 2 {
			return
		}
		if count > 2 {
			if !candExistInOnlyTwoCols(rows, rowNums, colNums, cand) {
				return
			}
		}
	}
	ok = true
	for _, rowNum := range rowNums {
		for _, colNum := range colNums {
			crosslines = append(crosslines, rows[rowNum].cells[colNum])
		}
	}

	return
}

func existMissingCandSolved(rows []House, rowNums []int, colNum int, missCand int) bool {
	for _, rowNum := range rowNums {
		if rows[rowNum].cells[colNum].value == toValue(missCand) {
			return true
		}
	}
	return false
}

func candExistInOnlyTwoCols(rows []House, rowNums []int, colNums []int, cand int) bool {
	count := 0
	for _, colNum := range colNums {
		for _, rowNum := range rowNums {
			if rows[rowNum].cells[colNum].cands.Has(cand) {
				count++
				break
			}
		}
	}
	return count == 2
}

func (s *Solve) OneBaseJuniorExocet(g *Grid) (used bool, results []*Result) {
	for rowNum := range 9 {
		if ok, rs := g.oneBaseje(g.rows[:], rowNum); ok {
			used = true
			results = append(results, rs...)
		}
	}
	for colNum := range 9 {
		if ok, rs := g.oneBaseje(g.cols[:], colNum); ok {
			used = true
			results = append(results, rs...)
		}
	}
	return
}

func (g *Grid) oneBaseje(rows []House, baseRowNum int) (used bool, results []*Result) {
	baseColNums := []int{}
	for colNum, cell := range rows[baseRowNum].cells {
		if !cell.solved() && cell.cands.Count() == 2 {
			baseColNums = append(baseColNums, colNum)
		}
	}
	for _, baseColNum := range baseColNums {
		baseCell := rows[baseRowNum].cells[baseColNum]
		baseCands := baseCell.cands
		baseStackNum := baseColNum / 3
		targetStackNums := [2]int{(baseStackNum + 1) % 3, (baseStackNum + 2) % 3}
		targetRowNums := [2]int{baseRowNum/3*3 + (baseRowNum+1)%3, baseRowNum/3*3 + (baseRowNum+2)%3}
		for i, targetRowNum := range targetRowNums {
			for j, targetStackNum := range targetStackNums {
				for targetColNum := targetStackNum * 3; targetColNum < targetStackNum*3+3; targetColNum++ {
					targetCell := rows[targetRowNum].cells[targetColNum]
					if targetCell.solved() || !targetCell.cands.IsSuperset(baseCands) ||
						targetCell.cands == baseCands {
						continue
					}
					var rmCands set.Set
					companionCell := rows[targetRowNums[1-i]].cells[targetColNum]
					if !companionCell.solved() {
						if ok, onlyCands := g.findOnlyCandsInTargets(baseCands, targetCell, companionCell); ok {
							rmCands = baseCands.Complement().Diff(onlyCands)
						} else {
							continue
						}
					} else {
						rmCands = baseCands.Complement()
					}
					//fmt.Println("baseCell", baseCell, "targetCell", targetCell)
					crossline1ColNums := []int{}
					for k := range 3 {
						num := targetStackNums[1-j]*3 + k
						escapeCell := rows[baseRowNum].cells[num]
						if escapeCell.solved() || !escapeCell.cands.IsSuperset(baseCands) ||
							!rows[targetRowNums[0]].cells[num].solved() ||
							!rows[targetRowNums[1]].cells[num].solved() {
							continue
						}
						crossline1ColNums = append(crossline1ColNums, num)
					}
					if len(crossline1ColNums) == 0 {
						continue
					}
					//fmt.Println("crossline1ColNums", crossline1ColNums)

					crossline2ColNums := []int{}
					for i := range 2 {
						num := baseColNum/3*3 + (baseColNum+1+i)%3
						if !rows[baseRowNum].cells[num].solved() &&
							rows[baseRowNum].cells[num].cands.IsSuperset(baseCands) {
							crossline2ColNums = append(crossline2ColNums, num)
						}
					}
					crossline3ColNums := []int{targetColNum}
					tierNum := baseRowNum / 3
					crossTierNums := [2]int{(tierNum + 1) % 3, (tierNum + 2) % 3}
					crossRowNums := []int{}
					for _, tierNum := range crossTierNums {
						for i := range 3 {
							crossRowNums = append(crossRowNums, tierNum*3+i)
						}
					}
					//fmt.Println(crossRowNums, crossline1ColNums, crossline2ColNums, crossline3ColNums)
					for _, crossColNums := range zip(crossline1ColNums, crossline2ColNums, crossline3ColNums) {
						if ok, crossLines := verifyCrossLines(baseCands, rows, crossRowNums, crossColNums); ok {
							used = true
							result := new(Result)
							g1 := result.getHelpCandsGroup()
							g1.add(baseCands, []*Cell{baseCell, targetCell})
							g1.add(baseCands, crossLines)
							result.addRemoved(rmCands, []*Cell{targetCell})
							if companionCell.solved() {
							} else {
								result.addRemoved(rmCands, []*Cell{companionCell})
								g1.add(baseCands, []*Cell{companionCell})
							}
							result.addHelpCells([]*Cell{baseCell})
							result.addHelpCells([]*Cell{targetCell, companionCell})
							result.addHelpCells(crossLines)

							result.setStrategyName("One Base Junior Exocet")
							results = append(results, result)
						}
					}
				}
			}
		}

	}

	return
}

func (g *Grid) verifyJENaked(cands set.Set, zCells []*Cell, cells []*Cell) (
	used bool, subset []*Cell, subsetCands set.Set, rmCells []*Cell) {
	var ok bool
	ok, subset, subsetCands = searchNakedSubset(cands, cells)
	//fmt.Println("verifyJENaked", ok, subset, subsetCands)
	if !ok {
		return
	}
	for _, cell := range cells {
		if slices.Contains(subset, cell) {
			continue
		}
		if cell.canRemoveCands(subsetCands) {
			used = true
			rmCells = append(rmCells, cell)
		}
	}
	for _, cell := range zCells {
		if cell.canRemoveCands(subsetCands.Diff(cands)) {
			used = true
		}
	}
	return
}

func searchNakedSubset(cands set.Set, cells []*Cell) (ok bool, subset []*Cell, subsetCands set.Set) {
	for i := cands.Count() - 1; i < len(cells); i++ {
		for _, subset = range combine(cells, i) {
			subsetCands = cands.Union(unionCellCands(subset))
			if subsetCands.Count() == len(subset)+1 {
				ok = true
				return
			}
		}
	}
	return
}
